<template>
  <div class="picContainer">
    <div class="title">
      鼠标 x:{{ mousePosition.x.toFixed(1) }} y:{{ mousePosition.y.toFixed(1) }} z:{{ mousePosition.z.toFixed(1) }}
    </div>
    <div ref="threeBoxRef" class="box">

    </div>
    <template v-for="(o,index) in cubes" :key="index" >
      <div class="info1"  :style="o.dynamicStyle"> {{o.name}} </div>
    </template>
  </div>
</template>
<script lang="ts">
import {defineComponent, onMounted, reactive, ref, toRaw, toRefs} from 'vue';
import * as THREE from 'three';
// 引入扩展库OrbitControls.js
import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
// 引入扩展库GLTFLoader.js
import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
import {DoubleSide} from "three/src/constants";
import {Color} from "three/src/math/Color";
import {FontLoader} from 'three/addons/loaders/FontLoader.js'
import {TextGeometry} from 'three/addons/geometries/TextGeometry.js';
import sensorXYZ from "/@/views/iotApply/iotLocate/locate/component2/sensorXYZ.vue";
import {ElMessage} from "element-plus";
import {randomInt} from "/@/utils/strUtil.js";
import {
  calculateCircleXYByArc, computeScaleRate,
  defaultThreeData, saveFloatFixByScaleRate,
  sealHeadTypeMap
} from "/@/views/iotApply/iotLocate/locate/component2/commonThree";

export default defineComponent({
  name:"planeLocate3d",
  components:{
  },
  props:{
  },
  setup(props,{emit}) {
    const sensorXYZRef = ref()
    const threeBoxRef = ref()
    const info1Ref = ref()
    var mouse = new THREE.Vector2();
    let scene = new THREE.Scene();
    // 创建渲染器对象
    const renderer = new THREE.WebGLRenderer();
    // 实例化一个透视投影相机对象
    const camera = new THREE.PerspectiveCamera();
    const raycaster = new THREE.Raycaster();

    let gridMesh : THREE.GridHelper| null = null
    let trayMesh : THREE.Mesh| null = null
    let axisLineMeshes = []
    let axisTextMeshes = []

    const scenePositionOffset = {
      x: 0,
      y:0
    }

    const state = reactive({
      mousePosition: {
        x: 0, y: 0, z: 0
      },
      threeData:{
        width: 600,
        height: 600,
        compareSize:200,  //这个值用来进行对比比例
        bgColor:"#C0C0C0",
        scaleRate: 1,    // 记录form表单的数据，包括所有的坐标，应该以什么比例进行缩放。当数据接近或者大于width时，会明显变形，这个值就是为了避免这种情况。
      },
      pFormData:{ //用户原本的数据
        xrange:1000,
        yrange:1000,
        rowCount:4,
        columnCount:3,
        viewType:0,
        showColumns:["no","x","y"],
        tableData:[], //传感器用户坐标
        measureDistanceType:0,
      },
      formData:{  //转化后的视图数据
        xrange:1000,
        yrange:1000,
        rowCount:4,
        columnCount:3,
        viewType:0,
        showColumns:["no","x","y"],
        tableData:[], //传感器用户坐标
        measureDistanceType:0,
      },
      cubes:[

      ]
    });



    function RandomNumBoth(Min, Max) {
      var Range = Max - Min;
      var Rand = Math.random();
      var num = Min + Math.round(Rand * Range); //四舍五入
      return num;
    }

    // 页面加载时
    onMounted(() => {
      initThreeData()
      renderPicOnce()
    });

    const renderPicOnce = ()=>{
      window.addEventListener("mousemove", listenMouseMove)

      renderer.setSize(state.threeData.width, state.threeData.height); //设置three.js渲染区域的尺寸(像素px)
      document.body.appendChild(renderer.domElement);

      // 添加光源
      const light = new THREE.DirectionalLight(0xffffff, 1);
      light.position.set(0,state.threeData.width, state.threeData.height);
      scene.add(light);

      threeBoxRef.value.appendChild(renderer.domElement);

      // 添加轨道控制器
      const controls = new OrbitControls(camera, renderer.domElement)
      controls.addEventListener('change', function () {
        updateAllTextPosition()
        renderer.render(scene, camera); //执行渲染操作
      })


      //相机观察目标指向Threejs 3D空间中某个位置
      camera.lookAt(scene.position); //坐标原点
      // 设置相机的视口与渲染器的尺寸相同
      camera.aspect = renderer.domElement.width / renderer.domElement.height;
      camera.updateProjectionMatrix();

      scene.position.set(scenePositionOffset.x, scenePositionOffset.y, 0);
      scene.background = new THREE.Color(state.threeData.bgColor)
      camera.position.set(0, 0, 270);

      renderPic()
    }

    const renderPic = ()=>{
      // 清除画布
      clearCanvas();

      // 创建XYZ轴
      createAxis();

      createSensorBoxes()

      setTimeout(() => {
        renderer.render(scene, camera); //执行渲染操作
      }, 10)
    }

    function simulatePoint(){
      // 生成数据
      var data = generateData(20); // 生成100个随机数据点
      // 创建散点
      data.forEach(function (point) {
        const geometry = new THREE.BoxGeometry( 1, 1,point.depth  );
        const material = new THREE.MeshBasicMaterial( {color: 0xff0000} );
        const cube = new THREE.Mesh( geometry, material );
        scene.add( cube );
        cube.position.set(point.x, point.y, point.depth/2);
        scene.add(cube);
      });
    }

    function createSensorBoxes(){
      if(!state.formData || !state.formData.tableData){
        return
      }
      state.cubes.map((item,index)=>{
        if(item.cube){
          item.cube.removeFromParent()
        }
      })
      state.cubes = []
      let tmp = []
      state.formData.tableData.map((item:any,index)=>{
        item = Object.assign({},item)
        const cube = createCubeItem(5)
        const cubexyz = transferPlaneDataTo3dData(item.x,item.y,0)
        cube.position.set(cubexyz.axisX, cubexyz.axisY, cubexyz.axisZ); // 调整位置以适应您的需求
        scene.add(cube);
        tmp.push({
          name: item.no,
          dynamicStyle: {
            left: "0px",
            top: "0px",
          },
          cube:cube
        })
      })
      state.cubes = tmp
    }

    const transferPlaneDataTo3dData = (x:number,y:number,z:number) => {
      return {
        axisX: x - state.formData.xrange/2,
        axisY: y - state.formData.yrange/2,
        axisZ: z,
      }
    }

    function clearCanvas() {
      renderer.clear();
    }

    const initThreeData = ()=>{
      state.threeData = defaultThreeData()
    }


    function updateAllTextPosition(){
      state.cubes.map((item,index)=>{
        if(item.cube){
          updateTextPosition(index,item.cube)
        }
      })
    }

    function updateTextPosition(index, cube) {
      const vector = new THREE.Vector3();
      cube.getWorldPosition(vector);
      vector.project(camera);

      const x = (vector.x * .5 + .5) * state.threeData.width;
      const y = (-vector.y * .5 + .5) * state.threeData.height;

      state.cubes[index].dynamicStyle.left = x + "px"
      state.cubes[index].dynamicStyle.top = y + "px"
    }

    function createAxis() {
      var axisLabelFont;
      var fontLoader = new FontLoader();
      //'https://threejs.org/examples/fonts/helvetiker_regular.typeface.json'
      fontLoader.load("/threefont.json",function (font) { // 加载字体文件
        axisLabelFont = font;
        createAxisHelper(axisLabelFont);
      });
    }

    function createAxisHelper(font) {
      //清除
      if(axisLineMeshes.length>0){
        axisLineMeshes.map((item:THREE.Line)=>{
          item.removeFromParent()
        })
      }
      if(axisTextMeshes.length>0){
        axisTextMeshes.map((item:THREE.Line)=>{
          item.removeFromParent()
        })
      }
      axisLineMeshes = []
      axisTextMeshes = []

      var materialX = new THREE.LineBasicMaterial({ color: 0xff0000 }); // 红色X轴
      var materialY = new THREE.LineBasicMaterial({ color: 0x00ff00 }); // 绿色Y轴
      var materialZ = new THREE.LineBasicMaterial({ color: 0x0000ff }); // 蓝色Z轴
      var materialGridLine = new THREE.LineBasicMaterial({ color: 0xffffff });

      const xrange = Math.abs(state.formData.xrange)
      const xstep = Math.abs((state.formData.xrange) / 10)
      const xhalf = Math.abs(xrange / 2)

      const yrange = Math.abs(state.formData.yrange)
      const ystep = Math.abs((state.formData.yrange) / 10)
      const yhalf = Math.abs(yrange / 2)

      // X轴刻度和刻度值
      let xcount = 0
      for (var i = -xhalf; i <= xhalf; i+=xstep) {
        var geometry = new THREE.BufferGeometry();
        var positions = new Float32Array([
          0, 0, 0,
          0, yrange, 0
        ]);
        geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        var line = new THREE.Line(geometry, materialGridLine);
        axisLineMeshes.push(line)
        line.position.set(i, -yhalf, 0);
        scene.add(line);

        //避免横坐标重叠
        const textLabel = ((i+xhalf)* state.threeData.scaleRate).toFixed(0)
        var textGeometry = new TextGeometry(textLabel.toString(), {
          font: font,
          size: parseInt((xstep/5).toString()),
          height: 1
        });
        var textMaterial = new THREE.MeshBasicMaterial({ color: 0x888888 });
        var textMesh = new THREE.Mesh(textGeometry, textMaterial);
        textMesh.position.set(i-(xstep/4), (-ystep/2)-yhalf, 0.2);
        axisTextMeshes.push(textMesh)
        scene.add(textMesh);

        xcount++
      }

      // Y轴刻度和刻度值
      for (var i = -yhalf; i <= yhalf; i+=ystep) {
        var geometry = new THREE.BufferGeometry();
        var positions = new Float32Array([
          0, 0, 0,
          xrange, 0, 0
        ]);
        geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        var line = new THREE.Line(geometry, materialGridLine);
        axisLineMeshes.push(line)
        line.position.set(-xhalf, i, 0);
        scene.add(line);

        const textLabel = ((i+yhalf )* state.threeData.scaleRate).toFixed(0)
        var textGeometry = new TextGeometry(textLabel.toString(), {
          font: font,
          size: parseInt((ystep/5).toString()),
          height: 1
        });
        var textMaterial = new THREE.MeshBasicMaterial({ color: 0x888888 });
        var textMesh = new THREE.Mesh(textGeometry, textMaterial);
        textMesh.position.set(-xstep-xhalf, i, 0.2);
        axisTextMeshes.push(textMesh)
        scene.add(textMesh);
      }

      // // Z轴刻度和刻度值
      // for (var i = min; i <= min+20;  i+=step) {
      //     var geometry = new THREE.BufferGeometry();
      //     var positions = new Float32Array([
      //         0, 0, i,
      //         0.2, 0, i
      //     ]);
      //     geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
      //     var line = new THREE.Line(geometry, materialZ);
      //     scene.add(line);
      //
      //     var textGeometry = new TextGeometry(i.toString(), {
      //         font: font,
      //         size: 3,
      //         height:1
      //     });
      //     var textMaterial = new THREE.MeshBasicMaterial({ color: 0x888888 });
      //     var textMesh = new THREE.Mesh(textGeometry, textMaterial);
      //     textMesh.position.set(-0.5, 0.2, i);
      //     scene.add(textMesh);
      // }

      // XYZ轴线
      var xAxisGeometry = new THREE.BufferGeometry();
      var xAxisPositions = new Float32Array([
        0, 0, 0,
        xrange, 0, 0
      ]);
      xAxisGeometry.setAttribute('position', new THREE.BufferAttribute(xAxisPositions, 3));
      var xAxisLine = new THREE.Line(xAxisGeometry, materialX);
      axisLineMeshes.push(xAxisLine)
      xAxisLine.position.set(-xhalf, -yhalf, 0);
      scene.add(xAxisLine);

      var yAxisGeometry = new THREE.BufferGeometry();
      var yAxisPositions = new Float32Array([
        0, 0, 0,
        0, yrange, 0
      ]);
      yAxisGeometry.setAttribute('position', new THREE.BufferAttribute(yAxisPositions,  3));
      var yAxisLine = new THREE.Line(yAxisGeometry, materialY);
      axisLineMeshes.push(yAxisLine)
      yAxisLine.position.set(-xhalf, -yhalf, 0);
      scene.add(yAxisLine);

      // var zAxisGeometry = new THREE.BufferGeometry();
      // var zAxisPositions = new Float32Array([
      //     0, 0, min,
      //     0, 0, max+step
      // ]);
      // zAxisGeometry.setAttribute('position', new THREE.BufferAttribute(zAxisPositions,  3));
      // var zAxisLine = new THREE.Line(zAxisGeometry, materialZ);
      // scene.add(zAxisLine);

      renderer.render(scene, camera); //执行渲染操作
    }
    // 生成随机数据点
    function generateData(count:number) {
      var data = [];
      for (var i = 0; i < count; i++) {
        var x = randomInt(0,100)
        var y =  randomInt(0,100)
        var z = 0;
        var depth = randomInt(1,20)
        data.push({ x: x, y: y, z: z ,depth:depth});
      }
      return data;
    }


    function createCubeItem(size:number) {
      const cubeGeometry = new THREE.BoxGeometry(size, size, size); // 正方体的尺寸
      const material = new THREE.MeshBasicMaterial({color: "#0000ee"});
      const cube = new THREE.Mesh(cubeGeometry, material);
      return cube;
    }

    function addPoints() {
      const geometry = new THREE.SphereGeometry(20, 8, 8);
      const material = new THREE.MeshBasicMaterial({color: 0xff0000});
      const sphere = new THREE.Mesh(geometry, material);
      sphere.position.set(RandomNumBoth(-400, 400), RandomNumBoth(-400, 400), RandomNumBoth(-100, 100)); // 调整位置以适应您的需求
      scene.add(sphere);
    }

    const listenMouseMove = (event: any) => {
      const px = event.offsetX;
      const py = event.offsetY;
      //屏幕坐标px、py转标准设备坐标x、y
      //width、height表示canvas画布宽高度
      const x = (px / state.threeData.width) * 2 - 1;
      const y = -(py / state.threeData.height) * 2 + 1;
      mouse.x = x
      mouse.y = y
      // 将鼠标坐标转换为世界坐标
      raycaster.setFromCamera(mouse, camera);
      // 使用光线投射器来获取与射线相交的物体
      const intersects = raycaster.intersectObjects(scene.children, true);
      if (intersects.length > 0) {
        var intersection = intersects[0];
        var position = intersection.point;
        // 在控制台中打印三维坐标
        //console.log('Mouse Position:', position);

        position.x = Number(position.x)*state.threeData.scaleRate + (state.formData.xrange/2)*state.threeData.scaleRate
        position.y = Number(position.y)*state.threeData.scaleRate + (state.formData.yrange/2)*state.threeData.scaleRate
        position.z = Number(position.z)*state.threeData.scaleRate
        state.mousePosition = position
      }
    }

    const reRender = (pFormData:any)=>{
      updateFormData(pFormData)
      renderPic()
      updateAllTextPosition()
      renderer.render(scene, camera); //执行渲染操作
    }

    const updateFormData = (pFormData:any) => {
      state.pFormData = Object.assign({},pFormData)
      changePForm2Form()
    }

    const changePForm2Form = ()=> {
      const maxdata = getPFormMaxData(state.pFormData)
      const scaleRate = computeScaleRate(maxdata,state.threeData.compareSize)
      state.threeData.scaleRate = scaleRate
      const tofixed = saveFloatFixByScaleRate(scaleRate)
      state.formData = Object.assign({},state.pFormData)
      state.formData.xrange = Number((Number(state.pFormData.xrange) / scaleRate).toFixed(tofixed))
      state.formData.yrange = Number((Number(state.pFormData.yrange) / scaleRate).toFixed(tofixed))

      if(state.pFormData.tableData){
        let tabledataTmp = []
        state.pFormData.tableData.map((item:any,index:number)=>{
          const copyItem = Object.assign({},item)
          copyItem.x = Number((Number(item.x) / scaleRate).toFixed(tofixed))
          copyItem.y = Number((Number(item.y) / scaleRate).toFixed(tofixed))
          tabledataTmp.push(copyItem)
        })
        state.formData.tableData = tabledataTmp
      }
    }

    const getPFormMaxData = (pFormData:any)=>{
      let max =pFormData.xrange
      if(pFormData.yrange > max){
        max = pFormData.yrange
      }
      return max
    }

    return {
      threeBoxRef,
      info1Ref,
      ...toRefs(state),
      reRender,
    }
  }
})
</script>
<style scoped lang="scss">
.picContainer{
  display: flex;
  flex-direction: column;
  position: relative;
  .title{
    width: 600px;
    text-align: center;
  }
  .box{

  }
}
.info1 {
  position: absolute;
  top: 0;
  z-index: 100;
  display:inline-block;
  line-height: 1;
  color:orangered;
  font-weight: bold;
  font-size: 13px;
}
body {
  margin: 0;
}

canvas {
  display: block;
}

.label {
  position: absolute;
  color: black;
  font-size: 14px;
}
</style>





